<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Symbol的使用过程</title>
  <!-- 
  `Symbol`  是 JavaScript ES6 引入的一种新的基本数据类型，用于创建唯一的标识符。与其他基本数据类型（如  `string` 、 `number` 、 `boolean` 、 `null`  和  `undefined` ）不同， `Symbol`  的值是唯一且不可变的。
  一、创建 Symbol
  1. 基本用法：
    使用  `Symbol()`  函数可以创建一个新的 Symbol。可以传入一个可选的描述字符串，主要用于调试。
  const sym1 = Symbol('description1');
    const sym2 = Symbol('description2');

    console.log(sym1); // 输出: Symbol(description1)
    console.log(sym2); // 输出: Symbol(description2)
    console.log(sym1 === sym2); // 输出: false，两个 Symbol 是不同的
  二、Symbol 的特点
  1. 唯一性：
    每个 Symbol 都是唯一的，即使它们的描述相同。
    const sym1 = Symbol('test');
    const sym2 = Symbol('test');
    console.log(sym1 === sym2); // 输出: false
  2. 不可变性：
    Symbol 的值是不可变的，不能被修改。

  三、Symbol 的使用场景
  1. 对象属性的唯一标识符：
    Symbol 可以用作对象的属性键，确保属性的唯一性，避免属性名冲突。
    const symKey = Symbol('key');
    const obj = {
        [symKey]: 'value'
    };

    console.log(obj[symKey]); // 输出: value
  2. 避免属性冲突：
    当多个库或模块需要在同一个对象上添加属性时，使用 Symbol 可以避免属性名冲突。
    const sym1 = Symbol('lib1');
    const sym2 = Symbol('lib2');

    const obj = {
        [sym1]: 'Library 1',
        [sym2]: 'Library 2'
    };

    console.log(obj[sym1]); // 输出: Library 1
    console.log(obj[sym2]); // 输出: Library 2
  3. 定义私有属性：
    虽然 Symbol 不能完全实现私有属性，但可以用来创建不易被外部访问的属性。
    const privateKey = Symbol('private');
    class MyClass {
        constructor() {
            this[privateKey] = 'secret';
        }

        getSecret() {
            return this[privateKey];
        }
    }

    const instance = new MyClass();
    console.log(instance.getSecret()); // 输出: secret
    console.log(instance.privateKey); // 输出: undefined

  四、全局 Symbol 注册
  1. Symbol.for()：
    使用  `Symbol.for(key)`  可以在全局注册一个 Symbol，若已存在相同的 key，则返回已注册的 Symbol。
    const globalSym1 = Symbol.for('globalKey');
    const globalSym2 = Symbol.for('globalKey');

    console.log(globalSym1 === globalSym2); // 输出: true
  2. Symbol.keyFor()：
    使用  `Symbol.keyFor(symbol)`  可以获取全局注册的 Symbol 的 key。
    const sym = Symbol.for('myGlobalSymbol');
    console.log(Symbol.keyFor(sym)); // 输出: myGlobalSymbol

  五、总结
  -  `Symbol`  是一种新的基本数据类型，主要用于创建唯一的标识符。
  - 每个 Symbol 都是唯一且不可变的，适合用作对象的属性键，避免属性名冲突。
  - 可以通过  `Symbol.for()`  在全局范围注册 Symbol，使用  `Symbol.keyFor()`  获取其对应的 key。
  -  `Symbol`  提供了一种有效的方式来处理对象属性，特别是在大型应用程序或库中，确保属性的唯一性和避免命名冲突。
  -->
</head>
<body>

  <script>
    // es6之前的属性名都是字符串，容易造成属性名冲突
    // const school = {
    //   name: 'school',
    //   address: '北京',
    //   name: 'ATGUIGU'
    // }
    // console.log(school);

    // 添加新属性
    // function foo(school) {
    //   school.fn = function() {}
    // }
    // foo(school)
    // console.log(obj.fn);
    
    // 1. es6新增的symbol类型，用来定义独一无二的属性名
    const s1 = Symbol()
    const info = {
      [s1]: 'Symbol'
    }

    const s2 = Symbol()
    info[s2] = 'Symbol2'
    console.log(info);

    // 2. Symbol的描述
    const s3 = Symbol('atguigu')
    const s4 = Symbol('atguigu')
    console.log(s3 === s4); // false

    // 3. Symbol值作为对象属性名时，不能用点运算符
    const s5 = Symbol()
    const obj = {
      [s5]: 'hello'
    }
    console.log(obj[s5]); // hello

    // 4. Symbol值作为对象属性名，不能用for...in循环遍历，但可以用Reflect.ownKeys()方法获取
    const s6 = Symbol()
    const obj2 = {
      [s6]: 'hello',
      age: 18
    }
    console.log(Reflect.ownKeys(obj2)); // [Symbol(), 'age']

    // 5. Symbol.for()方法
    const s7 = Symbol.for('atguigu')
    const s8 = Symbol.for('atguigu')
    console.log(s7 === s8); // true

    // Symbol.keyFor()方法
    const s9 = Symbol.for('atguigu')
    console.log(Symbol.keyFor(s9)); // atguigu

    // 6. Symbol的内置属性description
    const s10 = Symbol('sybkey')
    console.log(s10.description); // sybkey

    // 函数封装
    function symbolFn(obj) {
      const sKey = Symbol()
      obj[sKey] = 'hello'
      delete obj[sKey]
    }
    symbolFn(info)
    
  </script>
  
</body>
</html>